"""The executable of pdftocio"""

import sys
import os.path
import pdftocio
import getopt
import io

from typing import Optional, TextIO
from getopt import GetoptError
from fitzutils import open_pdf, dump_toc, pprint_toc, get_file_encoding
from .tocparser import parse_toc
from .tocio import write_toc, read_toc

usage_s = """
usage: pdftocio [options] in.pdf < toc
       pdftocio [options] in.pdf
""".strip()

help_s = r"""
usage: pdftocio [options] in.pdf < toc
       pdftocio [options] in.pdf

Import/output the table of contents of a PDF file.

This command can operate in two ways: it can either be used
to extract the table of contents of a PDF, or import table
of contents to a PDF using the output of pdftocgen.

1. To extract the table of contents of a PDF for
   modification, only supply a input file:

     $ pdftocio in.pdf

   or if you want to print it in a readable format, use the
   -H flag:

     $ pdftocio -H in.pdf

2. To import a table of contents to a PDF using the toc file
   generated by pdftocgen, use input redirection,

     $ pdftocio in.pdf < toc

   pipes,

     $ pdftocgen -r recipe.toml in.pdf | pdftocio in.pdf

   or the -t flag

     $ pdftocio -t toc in.pdf

   to supply the toc file. If you want to specify an output
   file name, use the -o option

     $ pdftocio -t toc -o out.pdf in.pdf

arguments
  in.pdf                path to the input PDF document

options
  -h, --help            show help
  -t, --toc=toc         path to the table of contents generated by
                        pdftocgen. if this option is not given, the
                        default is stdin, but if no input is piped or
                        redirected to stdin, this program will instead
                        print the existing ToC of the PDF file
  -v, --vpos            if this flag is set, the vertical position of
                        each heading will be dumped to the output
  -p, --print           when flag is set, print the existing ToC in
                        the input PDF file. this flag is usually not
                        necessary, since it is the default behavior
                        when no input is given
  -H, --human-readable  print the toc in a readable format
  -o, --out=file.pdf    path to the output file. if this flag is not
                        specified, the default is {input}_out.pdf
  -g, --debug           enable debug mode
  -V, --version         show version number

[1]: https://krasjet.com/voice/pdf.tocgen/#step-1-build-a-recipe
""".strip()


def main():
    # parse arguments
    try:
        opts, args = getopt.gnu_getopt(
            sys.argv[1:],
            "hvt:pHo:gV",
            ["help", "vpos", "toc=", "print", "human-readable", "out=", "debug", "version"]
        )
    except GetoptError as e:
        print(e, file=sys.stderr)
        print(usage_s, file=sys.stderr)
        sys.exit(2)

    toc_file: TextIO = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='ignore')
    print_toc: bool = False
    readable: bool = False
    out: Optional[str] = None
    vpos: bool = False
    debug: bool = False

    for o, a in opts:
        if o in ("-H", "--human-readable"):
            readable = True
        elif o in ("-p", "--print"):
            print_toc = True
        elif o in ("-v", "--vpos"):
            vpos = True
        elif o in ("-t", "--toc"):
            try:
                toc_file = open(a, "r", encoding=get_file_encoding(a))
            except IOError as e:
                print("error: can't open file for reading", file=sys.stderr)
                print(e, file=sys.stderr)
                sys.exit(1)
        elif o in ("-o", "--out"):
            out = a
        elif o in ("-g", "--debug"):
            debug = True
        elif o in ("-V", "--version"):
            print("pdftocio", pdftocio.__version__, file=sys.stderr)
            sys.exit()
        elif o in ("-h", "--help"):
            print(help_s, file=sys.stderr)
            sys.exit()

    if len(args) < 1:
        print("error: no input pdf is given", file=sys.stderr)
        print(usage_s, file=sys.stderr)
        sys.exit(1)

    path_in: str = args[0]
    # done parsing arguments

    try:
        with open_pdf(path_in) as doc:
            if toc_file.isatty() or print_toc:
                # no input from user, switch to output mode and extract the toc
                # of pdf
                toc = read_toc(doc)
                if len(toc) == 0:
                    print("error: no table of contents found", file=sys.stderr)
                    sys.exit(1)

                stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='ignore')

                if readable:
                    print(pprint_toc(toc), file=stdout)
                else:
                    print(dump_toc(toc, vpos), end="", file=stdout)
                sys.exit(0)

            # an input is given, so switch to input mode
            toc = parse_toc(toc_file)
            write_toc(doc, toc)

            if out is None:
                # add suffix to input name as output
                pfx, ext = os.path.splitext(path_in)
                out = f"{pfx}_out{ext}"
            doc.save(out)
    except ValueError as e:
        if debug:
            raise e
        print("error:", e, file=sys.stderr)
        sys.exit(1)
    except IOError as e:
        if debug:
            raise e
        print("error: unable to open file", file=sys.stderr)
        print(e, file=sys.stderr)
        sys.exit(1)
    except IndexError as e:
        if debug:
            raise e
        print("index error:", e, file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt as e:
        if debug:
            raise e
        print("error: interrupted", file=sys.stderr)
        sys.exit(1)
